import Tree from "./Tree";
import Client from "./Client";
import { ntlmUtil, Smb2Header, Smb2Dialect, Smb2PacketType } from "@dove/samba_protocol";
import buffer from '@ohos.buffer';
import { EventEmitter } from 'events';

export interface AuthenticateOptions {
  domain: string;
  username: string;
  password: string;
}

interface Session {
  on(event: "authenticate" | "logoff", callback: (session: Session) => void): this;

  once(event: "authenticate" | "logoff", callback: (session: Session) => void): this;
}

class Session extends EventEmitter {
  _id: string;
  connectedTrees: Tree[] = [];
  authenticated: boolean = false;

  constructor(public client: Client) {
    super();
  }

  async connectTree(path: string) {
    const tree = new Tree(this);
    this.registerTree(tree);
    await tree.connect(path);
    return tree;
  }

  createRequest(header: Smb2Header = {}, body: any = {}) {
    return this.client.createRequest({ sessionId: this._id, ...header }, body);
  }

  async request(header: Smb2Header = {}, body: any = {}) {
    return await this.client.request({ sessionId: this._id, ...header }, body);
  }

  async authenticate(options: AuthenticateOptions) {
    if (this.authenticated) return;
    await this.request({ type: Smb2PacketType.Negotiate }, {
      dialects: [Smb2Dialect.Smb202, Smb2Dialect.Smb210] });
    const sessionSetupResponse = await this.request({ type: Smb2PacketType.SessionSetup },
      { buffer: ntlmUtil.encodeNegotiationMessage(this.client.host, options.domain) });
    this._id = sessionSetupResponse.header.sessionId;
    const nonce = ntlmUtil.decodeChallengeMessage(sessionSetupResponse.body.buffer as buffer.Buffer);
    await this.request({ type: Smb2PacketType.SessionSetup }, {
      buffer: ntlmUtil.encodeAuthenticationMessage(options.username, this.client.host, options.domain, nonce, options.password)
    });
    this.authenticated = true;
    this.emit("authenticate", this);
  }

  private registerTree(tree: Tree) {
    tree.once("connect", () => this.connectedTrees.push(tree))
      .once("disconnect", () => this.connectedTrees.splice(this.connectedTrees.indexOf(tree), 1));
  }

  async logoff() {
    if (!this.authenticated) return;
    this.authenticated = false;
    await Promise.all(this.connectedTrees.map(x => x.disconnect()));
    await this.request({ type: Smb2PacketType.LogOff });
    delete this._id;
    this.emit("logoff", this);
  }
}
export default Session;